/* HackerPort driver for Linux - test application
 * version 0.7.2 date 2007-05-04
 * part of the hacker port project - http[s]://stefan.schuermans.info/hackerport/
 * Copyright (C) 2003-2004 stefan <stefan@schuermans.info>
 * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/poll.h>

#include "hkp_drv.h"



//maximum number of actions to perform at the same time
#define act_cnt_max 32



//macro for number of elements in an array
#define count( array ) (sizeof( (array) ) / sizeof( (array)[0] ))



//get first word and rest of a string
static void str_first_word( char * p_string, char * * pp_first, char * * pp_rest )
{
  //jump over whitespaces
  while( *p_string == '\t' || *p_string == ' ' )
    p_string++;

  //first word starts here
  *pp_first = p_string;

  //jump over first word
  while( *p_string != 0 && *p_string != '\t' && *p_string != ' ' )
    p_string++;

  //first word ends here
  if( *p_string != 0 )
  {
    *p_string = 0;
    p_string++;
  }

  //jump over whitespaces
  while( *p_string == '\t' || *p_string == ' ' )
    p_string++;

  //rest starts here
  *pp_rest = p_string;
}



//action: input value from analog input A
static int act_analog_a( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as analog_a
  p_act->type = hkp_drv_act_analog_a;
  return 0;
}

//action: input value from analog input B
static int act_analog_b( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as analog_b
  p_act->type = hkp_drv_act_analog_b;
  return 0;
}

//action: input value from analog input C
static int act_analog_c( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as analog_c
  p_act->type = hkp_drv_act_analog_c;
  return 0;
}

//action: input value from analog input D
static int act_analog_d( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as analog_d
  p_act->type = hkp_drv_act_analog_d;
  return 0;
}

//action: input value from analog input E
static int act_analog_e( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as analog_e
  p_act->type = hkp_drv_act_analog_e;
  return 0;
}

//action: input value from all analog inputs
static int act_analog_all( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as analog_all
  p_act->type = hkp_drv_act_analog_all;
  return 0;
}

//action: burst output 16 values on output port A
static int act_burst_out_a( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, idx, vals[count( p_act->data.burst_out.steps )];

  //get values
  for( idx = 0; idx < count( vals ); idx++ )
  {
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
    {
      printf( "  error: too few parameters\n" );
      return -1;
    }
    i = sscanf( p_param, "%i", &vals[idx] );
    if( i != 1 || vals[idx] < 0 || vals[idx] > 255 )
    {
      printf( "  error: invalid value \"%s\"\n", p_param );
      return -1;
    }
  }

  //initialize action as burst_out_a
  p_act->type = hkp_drv_act_burst_out_a;
  for( idx = 0; idx < count( vals ); idx++ )
    p_act->data.burst_out.steps[idx].x = (unsigned char)vals[idx];
  return 0;
}

//action: burst output 16 values on output port B
static int act_burst_out_b( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, idx, vals[count( p_act->data.burst_out.steps )];

  //get values
  for( idx = 0; idx < count( vals ); idx++ )
  {
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
    {
      printf( "  error: too few parameters\n" );
      return -1;
    }
    i = sscanf( p_param, "%i", &vals[idx] );
    if( i != 1 || vals[idx] < 0 || vals[idx] > 255 )
    {
      printf( "  error: invalid value \"%s\"\n", p_param );
      return -1;
    }
  }

  //initialize action as burst_out_b
  p_act->type = hkp_drv_act_burst_out_b;
  for( idx = 0; idx < count( vals ); idx++ )
    p_act->data.burst_out.steps[idx].x = (unsigned char)vals[idx];
  return 0;
}

//action: burst output 16 values on all output ports
static int act_burst_out_all( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, idx, vals[count( p_act->data.burst_out_all.steps ) << 1];

  //get values
  for( idx = 0; idx < count( vals ); idx++ )
  {
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
    {
      printf( "  error: too few parameters\n" );
      return -1;
    }
    i = sscanf( p_param, "%i", &vals[idx] );
    if( i != 1 || vals[idx] < 0 || vals[idx] > 255 )
    {
      printf( "  error: invalid value \"%s\"\n", p_param );
      return -1;
    }
  }

  //initialize action as burst_out_all
  p_act->type = hkp_drv_act_burst_out_all;
  for( idx = 0; idx < count( vals ) >> 1; idx++ )
  {
    p_act->data.burst_out_all.steps[idx].a = (unsigned char)vals[idx << 1];
    p_act->data.burst_out_all.steps[idx].b = (unsigned char)vals[(idx << 1) + 1];
  }
  return 0;
}

//action: burst output and input 16 values on port A
static int act_burst_out_in_a( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, idx, vals[count( p_act->data.burst_out_in.steps )];

  //get values
  for( idx = 0; idx < count( vals ); idx++ )
  {
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
    {
      printf( "  error: too few parameters\n" );
      return -1;
    }
    i = sscanf( p_param, "%i", &vals[idx] );
    if( i != 1 || vals[idx] < 0 || vals[idx] > 255 )
    {
      printf( "  error: invalid value \"%s\"\n", p_param );
      return -1;
    }
  }

  //initialize action as burst_out_in_a
  p_act->type = hkp_drv_act_burst_out_in_a;
  for( idx = 0; idx < count( vals ); idx++ )
    p_act->data.burst_out_in.steps[idx].o_x = (unsigned char)vals[idx];
  return 0;
}

//action: burst output and input 16 values on port B
static int act_burst_out_in_b( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, idx, vals[count( p_act->data.burst_out_in.steps )];

  //get values
  for( idx = 0; idx < count( vals ); idx++ )
  {
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
    {
      printf( "  error: too few parameters\n" );
      return -1;
    }
    i = sscanf( p_param, "%i", &vals[idx] );
    if( i != 1 || vals[idx] < 0 || vals[idx] > 255 )
    {
      printf( "  error: invalid value \"%s\"\n", p_param );
      return -1;
    }
  }

  //initialize action as burst_out_in_b
  p_act->type = hkp_drv_act_burst_out_in_b;
  for( idx = 0; idx < count( vals ); idx++ )
    p_act->data.burst_out_in.steps[idx].o_x = (unsigned char)vals[idx];
  return 0;
}

//action: burst output and input 16 values on all ports
static int act_burst_out_in_all( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, idx, vals[count( p_act->data.burst_out_in_all.steps ) << 1];

  //get values
  for( idx = 0; idx < count( vals ); idx++ )
  {
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
    {
      printf( "  error: too few parameters\n" );
      return -1;
    }
    i = sscanf( p_param, "%i", &vals[idx] );
    if( i != 1 || vals[idx] < 0 || vals[idx] > 255 )
    {
      printf( "  error: invalid value \"%s\"\n", p_param );
      return -1;
    }
  }

  //initialize action as burst_out_in_all
  p_act->type = hkp_drv_act_burst_out_in_all;
  for( idx = 0; idx < count( vals ) >> 1; idx++ )
  {
    p_act->data.burst_out_in_all.steps[idx].o_a = (unsigned char)vals[idx << 1];
    p_act->data.burst_out_in_all.steps[idx].o_b = (unsigned char)vals[(idx << 1) + 1];
  }
  return 0;
}

//action: output an acknowledge on I2C
static int act_i2c_ack( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as i2c_ack
  p_act->type = hkp_drv_act_i2c_ack;
  return 0;
}

//action: output a not-acknowledge on I2C
static int act_i2c_nak( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as i2c_nak
  p_act->type = hkp_drv_act_i2c_nak;
  return 0;
}

//action: read a byte from I2C
static int act_i2c_read( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as i2c_read
  p_act->type = hkp_drv_act_i2c_read;
  return 0;
}

//action: set speed of I2C
static int act_i2c_speed( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 79 || val > 10000 )
  {
    printf( "  error: invalid kHz value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as i2c_speed
  p_act->type = hkp_drv_act_i2c_speed;
  p_act->data.i2c_speed.bit_time = (unsigned char)((10000 + val / 2) / val);
  return 0;
}

//action: output a start condition on I2C
static int act_i2c_start( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as i2c_start
  p_act->type = hkp_drv_act_i2c_start;
  return 0;
}

//action: output a stop condition on I2C
static int act_i2c_stop( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as i2c_stop
  p_act->type = hkp_drv_act_i2c_stop;
  return 0;
}

//action: write a to I2C
static int act_i2c_write( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 0 || val > 255 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as i2c_write
  p_act->type = hkp_drv_act_i2c_write;
  p_act->data.i2c_write.data = (unsigned char)val;
  return 0;
}

//action: input value from input port A
static int act_in_a( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as in_a
  p_act->type = hkp_drv_act_in_a;
  return 0;
}

//action: input value from input port B
static int act_in_b( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as in_b
  p_act->type = hkp_drv_act_in_b;
  return 0;
}

//action: input value from all input ports
static int act_in_all( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as in_all
  p_act->type = hkp_drv_act_in_all;
  return 0;
}

//action: get TDO state of JTAG master port
static int act_jtag_get( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as jtag_get
  p_act->type = hkp_drv_act_jtag_get;
  return 0;
}

//action: set nTRST, TMS, TCK, TDI states of JTAG master port
static int act_jtag_set( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;

  //initialize action as jtag_set
  p_act->type = hkp_drv_act_jtag_set;
  p_act->data.jtag_set.ntrst = HKP_DRV_JTAG_NO_CHANGE;
  p_act->data.jtag_set.tms = HKP_DRV_JTAG_NO_CHANGE;
  p_act->data.jtag_set.tck = HKP_DRV_JTAG_NO_CHANGE;
  p_act->data.jtag_set.tdi = HKP_DRV_JTAG_NO_CHANGE;

  //process parameters
  //set up port ownership request structure
  for( ; ; )
  {
    //get next parameter
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
      break;

    //known parameters
    if( strcasecmp( p_param, "ntrst-" ) == 0 )
      p_act->data.jtag_set.ntrst = HKP_DRV_JTAG_LOW;
    else if( strcasecmp( p_param, "ntrst+" ) == 0 )
      p_act->data.jtag_set.ntrst = HKP_DRV_JTAG_HIGH;
    else if( strcasecmp( p_param, "tms-" ) == 0 )
      p_act->data.jtag_set.tms = HKP_DRV_JTAG_LOW;
    else if( strcasecmp( p_param, "tms+" ) == 0 )
      p_act->data.jtag_set.tms = HKP_DRV_JTAG_HIGH;
    else if( strcasecmp( p_param, "tck-" ) == 0 )
      p_act->data.jtag_set.tck = HKP_DRV_JTAG_LOW;
    else if( strcasecmp( p_param, "tck+" ) == 0 )
      p_act->data.jtag_set.tck = HKP_DRV_JTAG_HIGH;
    else if( strcasecmp( p_param, "tdi-" ) == 0 )
      p_act->data.jtag_set.tdi = HKP_DRV_JTAG_LOW;
    else if( strcasecmp( p_param, "tdi+" ) == 0 )
      p_act->data.jtag_set.tdi = HKP_DRV_JTAG_HIGH;
    //unknown parameter
    else
    {
      printf( "  error: invalid bit state \"%s\"\n", p_param );
      return -1;
    }
  }

  return 0;
}

//action: output value on output port A
static int act_out_a( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 0 || val > 255 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as out_a
  p_act->type = hkp_drv_act_out_a;
  p_act->data.out.x = (unsigned char)val;
  return 0;
}

//action: output value on output port B
static int act_out_b( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 0 || val > 255 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as out_b
  p_act->type = hkp_drv_act_out_b;
  p_act->data.out.x = (unsigned char)val;
  return 0;
}

//action: output value on all output ports
static int act_out_all( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val_a, val_b;

  //get value for port A
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val_a );
  if( i != 1 || val_a < 0 || val_a > 255 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //get value for port B
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val_b );
  if( i != 1 || val_b < 0 || val_b > 255 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as out_all
  p_act->type = hkp_drv_act_out_all;
  p_act->data.out_all.a = (unsigned char)val_a;
  p_act->data.out_all.b = (unsigned char)val_b;
  return 0;
}

//action: get PGD state of PIC programming interface
static int act_pic_get( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  //initialize action as pic_get
  p_act->type = hkp_drv_act_pic_get;
  return 0;
}

//action: input bits on PIC programming interface
static int act_pic_in( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, cnt;

  //get bit count
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &cnt );
  if( i != 1 || cnt < 0 || cnt > 16 )
  {
    printf( "  error: invalid bit count \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as pic_in
  p_act->type = hkp_drv_act_pic_in;
  p_act->data.pic_out_in.cnt = (unsigned char)cnt;
  return 0;
}

//action: output bits on PIC programming interface
static int act_pic_out( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, cnt, value;

  //get bit count
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &cnt );
  if( i != 1 || cnt < 0 || cnt > 16 )
  {
    printf( "  error: invalid bit count \"%s\"\n", p_param );
    return -1;
  }

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &value );
  if( i != 1 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as pic_out
  p_act->type = hkp_drv_act_pic_out;
  p_act->data.pic_out_in.cnt = (unsigned char)cnt;
  p_act->data.pic_out_in.data = (unsigned short)value;
  return 0;
}

//action: set VPP, PGC, PGD states of PIC programming interface
static int act_pic_set( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;

  //initialize action as pic_set
  p_act->type = hkp_drv_act_pic_set;
  p_act->data.pic_set.vpp = HKP_DRV_PIC_VPP_NO_CHANGE;
  p_act->data.pic_set.pgc = HKP_DRV_PIC_PGC_NO_CHANGE;
  p_act->data.pic_set.pgd = HKP_DRV_PIC_PGD_NO_CHANGE;

  //process parameters
  //set up port ownership request structure
  for( ; ; )
  {
    //get next parameter
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
      break;

    //known parameters
    if( strcasecmp( p_param, "vpp0" ) == 0 )
      p_act->data.pic_set.vpp = HKP_DRV_PIC_VPP_0V;
    else if( strcasecmp( p_param, "vpp13" ) == 0 )
      p_act->data.pic_set.vpp = HKP_DRV_PIC_VPP_13V;
    else if( strcasecmp( p_param, "vpp*" ) == 0 )
      p_act->data.pic_set.vpp = HKP_DRV_PIC_VPP_OPEN;
    else if( strcasecmp( p_param, "pgc-" ) == 0 )
      p_act->data.pic_set.pgc = HKP_DRV_PIC_PGC_LOW;
    else if( strcasecmp( p_param, "pgc+" ) == 0 )
      p_act->data.pic_set.pgc = HKP_DRV_PIC_PGC_HIGH;
    else if( strcasecmp( p_param, "pgd-" ) == 0 )
      p_act->data.pic_set.pgd = HKP_DRV_PIC_PGD_LOW;
    else if( strcasecmp( p_param, "pgd+" ) == 0 )
      p_act->data.pic_set.pgd = HKP_DRV_PIC_PGD_HIGH;
    else if( strcasecmp( p_param, "pgd*" ) == 0 )
      p_act->data.pic_set.pgd = HKP_DRV_PIC_PGD_OPEN;
    //unknown parameter
    else
    {
      printf( "  error: invalid bit state \"%s\"\n", p_param );
      return -1;
    }
  }

  return 0;
}

//action: output value on PWM output A
static int act_pwm_a( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 0 || val > 1000 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as pwm_a
  p_act->type = hkp_drv_act_pwm_a;
  p_act->data.pwm.x = (unsigned short)val;
  return 0;
}

//action: output value on PWM output B
static int act_pwm_b( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 0 || val > 1000 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as pwm_b
  p_act->type = hkp_drv_act_pwm_b;
  p_act->data.pwm.x = (unsigned short)val;
  return 0;
}

//action: output value on all PWM outputs
static int act_pwm_all( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val_a, val_b;

  //get value for output A
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val_a );
  if( i != 1 || val_a < 0 || val_a > 1000 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //get value for output B
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val_b );
  if( i != 1 || val_b < 0 || val_b > 1000 )
  {
    printf( "  error: invalid value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as pwm_all
  p_act->type = hkp_drv_act_pwm_all;
  p_act->data.pwm_all.a = (unsigned short)val_a;
  p_act->data.pwm_all.b = (unsigned short)val_b;
  return 0;
}

//action: set speed of PWM
static int act_pwm_speed( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_param;
  int i, val;

  //get value
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &val );
  if( i != 1 || val < 250 || val > 50000 )
  {
    printf( "  error: invalid Hz value \"%s\"\n", p_param );
    return -1;
  }

  //initialize action as pwm_speed
  p_act->type = hkp_drv_act_pwm_speed;
  p_act->data.pwm_speed.freq = (unsigned char)((val + 125) / 250);
  return 0;
}



//help action prototype
static int act_help( char * p_params, int quiet, struct t_hkp_drv_act * p_act );

//action table
struct t_act_tab
{
  char * name;
  char * parameters;
  char * description;
  int (* function)( char * p_params, int quiet, struct t_hkp_drv_act * p_act ); //returns 0 on success
} act_tab[] =
{
  { "analog_a", "", "input value from analog input A", act_analog_a },
  { "analog_b", "", "input value from analog input B", act_analog_b },
  { "analog_c", "", "input value from analog input C", act_analog_c },
  { "analog_d", "", "input value from analog input D", act_analog_d },
  { "analog_e", "", "input value from analog input E", act_analog_e },
  { "analog_all", "", "input value from all analog inputs", act_analog_all },
  { "burst_out_a", " <value 0> ... <value 15>", "burst output 16 values on output port A", act_burst_out_a },
  { "burst_out_b", " <value 0> ... <value 15>", "burst output 16 values on output port B", act_burst_out_b },
  { "burst_out_all", " <value_A 0> <value_B 0> ... <value_A 15> <value_B 15>", "burst output 16 values on all output ports", act_burst_out_all },
  { "burst_out_in_a", " <value 0> ... <value 15>", "burst output and input 16 values on port A", act_burst_out_in_a },
  { "burst_out_in_b", " <value 0> ... <value 15>", "burst output and input 16 values on port B", act_burst_out_in_b },
  { "burst_out_in_all", " <value_A 0> <value_B 0> ... <value_A 15> <value_B 15>", "burst output and input 16 values on all ports", act_burst_out_in_all },
  { "end", "", "end entering action(s)", NULL },
  { "exit", "", "end entering action(s)", NULL },
  { "help", " {<action>}", "show help (you already found out what this does)", act_help },
  { "i2c_ack", "", "output an acknowledge on I2C", act_i2c_ack },
  { "i2c_nak", "", "output a not-acknowledge on I2C", act_i2c_nak },
  { "i2c_read", "", "read a byte from I2C", act_i2c_read },
  { "i2c_speed", " <kHz>", "set speed of I2C", act_i2c_speed },
  { "i2c_start", "", "output a start condition on I2C", act_i2c_start },
  { "i2c_stop", "", "output a stop condition on I2C", act_i2c_stop },
  { "i2c_write", " <data>", "write a byte to I2C", act_i2c_write },
  { "in_a", "", "input value from input port A", act_in_a },
  { "in_b", "", "input value from input port B", act_in_b },
  { "in_all", "", "input value from all input ports", act_in_all },
  { "jtag_get", "", "get TDO state of JTAG master port", act_jtag_get },
  { "jtag_set", " {ntrst|tms|tck|tdi}[-+]", "set nTRST, TMS, TCK, TDI states of JTAG master port", act_jtag_set },
  { "out_a", " <value>", "output value on output port A", act_out_a },
  { "out_b", " <value>", "output value on output port B", act_out_b },
  { "out_all", " <value_a> <value_b>", "output value on all output ports", act_out_all },
  { "pic_get", "", "get PGD state of PIC programming interface", act_pic_get },
  { "pic_in", " <cnt>", "input bits on PIC programming interface", act_pic_in },
  { "pic_out", " <cnt> <value>", "output bits on PIC programming interface", act_pic_out },
  { "pic_set", " {vpp0|vpp13|vpp*|pgc[-+]|pgd[-+*]}", "set VPP, PGC, PGD states of PIC programming interface", act_pic_set },
  { "pwm_a", " <value>", "output value on PWM output A", act_pwm_a },
  { "pwm_b", " <value>", "output value on PWM output B", act_pwm_b },
  { "pwm_all", " <value_a> <value_b>", "output value on all PWM outputs", act_pwm_all },
  { "pwm_speed", " <Hz>", "set speed of PWM", act_pwm_speed },
  { "quit", "", "end entering action(s)", NULL },
};

//help action
static int act_help( char * p_params, int quiet, struct t_hkp_drv_act * p_act )
{
  char * p_act_txt;
  int i;

  //get action
  str_first_word( p_params, &p_act_txt, &p_params );

  //action found in parameters
  if( p_act_txt[0] != 0 )
  {
    //search action
    for( i = 0; i < count( act_tab ); i++ )
      if( strcasecmp( p_act_txt, act_tab[i].name ) == 0 )
        break;
    //action found in table
    if( i < count( act_tab ) )
      //print help for this action
      printf( "  %s%s\n    %s\n", act_tab[i].name, act_tab[i].parameters, act_tab[i].description );
    //action not found in table
    else
      //print error message
      printf( "  no help found to \"%s\"\n", p_act_txt );
    //return error to wait for action to be entered
    return -1;
  }

  //print action table and descriptions
  for( i = 0; i < count( act_tab ); i++ )
    printf( "  %s%s\n    %s\n", act_tab[i].name, act_tab[i].parameters, act_tab[i].description );

  //return error to wait for action to be entered
  return -1;
}


//read an action
//returns 0 on success, -1 on error
static int act_read( char * prompt_str, struct t_hkp_drv_act * p_act, int quiet )
{
  int i;
  char line[1024], * p_chr, * p_act_txt, * p_params;

  //loop until an action was recognized
  for( ; ; )
  {
    //read a line
    if( ! quiet ) //prompt
    {
      printf( "hacker port - action%s> ", prompt_str );
      fflush( stdout );
    }
    line[0] = 0; //read line
    fgets( line, sizeof( line ), stdin );
    p_chr = strchr( line, '\n' ); //remove newline
    if( p_chr != NULL )
      *p_chr = 0;

    //get action (first word of line)
    str_first_word( line, &p_act_txt, &p_params );

    //ignore empty command
    if( p_act_txt[0] != 0 )
    {
      //find action in action table
      for( i = 0; i < count( act_tab ); i++ )
        if( strcasecmp( p_act_txt, act_tab[i].name ) == 0 )
          break;
      //action found
      if( i < count( act_tab ) )
      {
        //cancel reading action loop if no function
        if( act_tab[i].function == NULL )
          return -1;
        //call command
        if( act_tab[i].function( p_params, quiet, p_act ) == 0 )
          //done if successful
          return 0;
      }
      //command not found
      else
        printf( "  unknown action - type \"help\"\n" );
    }
  }
}



//show result of action
static void act_result( char * prompt_str, struct t_hkp_drv_act * p_act, int quiet )
{
  int i;

  switch( p_act->type )
  {
    case hkp_drv_act_analog_a:
      printf( "  action%s: analog input A: %d = %4dmV\n",
              prompt_str, p_act->data.analog.x, p_act->data.analog.x * 5000 / 1023 );
      break;
    case hkp_drv_act_analog_b:
      printf( "  action%s: analog input B: %d = %4dmV\n",
              prompt_str, p_act->data.analog.x, p_act->data.analog.x * 5000 / 1023 );
      break;
    case hkp_drv_act_analog_c:
      printf( "  action%s: analog input C: %d = %4dmV\n",
              prompt_str, p_act->data.analog.x, p_act->data.analog.x * 5000 / 1023 );
      break;
    case hkp_drv_act_analog_d:
      printf( "  action%s: analog input D: %d = %4dmV\n",
              prompt_str, p_act->data.analog.x, p_act->data.analog.x * 5000 / 1023 );
      break;
    case hkp_drv_act_analog_e:
      printf( "  action%s: analog input E: %d = %4dmV\n",
              prompt_str, p_act->data.analog.x, p_act->data.analog.x * 5000 / 1023 );
      break;
    case hkp_drv_act_analog_all:
      printf( "  action%s: analog input A: %d = %4dmV\n"
              "  action%s: analog input B: %d = %4dmV\n"
              "  action%s: analog input C: %d = %4dmV\n"
              "  action%s: analog input D: %d = %4dmV\n"
              "  action%s: analog input E: %d = %4dmV\n",
              prompt_str, p_act->data.analog_all.a, p_act->data.analog_all.a * 5000 / 1023,
              prompt_str, p_act->data.analog_all.b, p_act->data.analog_all.b * 5000 / 1023,
              prompt_str, p_act->data.analog_all.c, p_act->data.analog_all.c * 5000 / 1023,
              prompt_str, p_act->data.analog_all.d, p_act->data.analog_all.d * 5000 / 1023,
              prompt_str, p_act->data.analog_all.e, p_act->data.analog_all.e * 5000 / 1023 );
      break;
    case hkp_drv_act_burst_out_a:
      printf( "  action%s: burst output on port A:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out.steps[i].x );
      printf( "\n" );
      break;
    case hkp_drv_act_burst_out_b:
      printf( "  action%s: burst output on port B:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out.steps[i].x );
      printf( "\n" );
      break;
    case hkp_drv_act_burst_out_all:
      printf( "  action%s: burst output on port A:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_all.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_all.steps[i].a );
      printf( "\n"
              "  action%s: burst output on port B:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_all.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_all.steps[i].b );
      printf( "\n" );
      break;
    case hkp_drv_act_burst_out_in_a:
      printf( "  action%s: burst output on port A:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in.steps[i].o_x );
      printf( "\n  action%s: burst input on port A: ", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in.steps[i].i_x );
      printf( "\n" );
      break;
    case hkp_drv_act_burst_out_in_b:
      printf( "  action%s: burst output on port B:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in.steps[i].o_x );
      printf( "\n  action%s: burst input on port B: ", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in.steps[i].i_x );
      printf( "\n" );
      break;
    case hkp_drv_act_burst_out_in_all:
      printf( "  action%s: burst output on port A:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in_all.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in_all.steps[i].o_a );
      printf( "\n"
              "  action%s: burst output on port B:", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in_all.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in_all.steps[i].o_b );
      printf( "\n"
              "  action%s: burst input on port A: ", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in_all.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in_all.steps[i].i_a );
      printf( "\n"
              "  action%s: burst input on port B: ", prompt_str );
      for( i = 0; i < count( p_act->data.burst_out_in_all.steps ); i++ )
        printf( " 0x%02X", p_act->data.burst_out_in_all.steps[i].i_b );
      printf( "\n" );
      break;
    case hkp_drv_act_i2c_ack:
      printf( "  action%s: I2C ack\n", prompt_str );
      break;
    case hkp_drv_act_i2c_nak:
      printf( "  action%s: I2C nak\n", prompt_str );
      break;
    case hkp_drv_act_i2c_read:
      printf( "  action%s: I2C read: 0x%02X\n",
              prompt_str, p_act->data.i2c_read.data );
      break;
    case hkp_drv_act_i2c_speed:
      printf( "  action%s: I2C speed: %d (= %dkHz)\n",
              prompt_str, p_act->data.i2c_speed.bit_time,
              (10000 + (unsigned int)p_act->data.i2c_speed.bit_time / 2) / (unsigned int)p_act->data.i2c_speed.bit_time );
      break;
    case hkp_drv_act_i2c_start:
      printf( "  action%s: I2C start\n", prompt_str );
      break;
    case hkp_drv_act_i2c_stop:
      printf( "  action%s: I2C stop\n", prompt_str );
      break;
    case hkp_drv_act_i2c_write:
      printf( "  action%s: I2C write: 0x%02X %s\n",
              prompt_str, p_act->data.i2c_write.data, p_act->data.i2c_write.acked ? "ack" : "nak" );
      break;
    case hkp_drv_act_in_a:
      printf( "  action%s: input port A: %d = 0x%02X\n",
              prompt_str, p_act->data.in.x, p_act->data.in.x );
      break;
    case hkp_drv_act_in_b:
      printf( "  action%s: input port B: %d = 0x%02X\n",
              prompt_str, p_act->data.in.x, p_act->data.in.x );
      break;
    case hkp_drv_act_in_all:
      printf( "  action%s: input port A: %d = 0x%02X\n"
              "  action%s: input port B: %d = 0x%02X\n",
              prompt_str, p_act->data.in_all.a, p_act->data.in_all.a,
              prompt_str, p_act->data.in_all.b, p_act->data.in_all.b );
      break;
    case hkp_drv_act_jtag_get:
      printf( "  action%s: JTAG get: TDO=%s\n",
              prompt_str, p_act->data.jtag_get.tdo == HKP_DRV_JTAG_HIGH ? "+" : "-" );
      break;
    case hkp_drv_act_jtag_set:
      {
        char * ntrst = "", * tms = "", * tck = "", * tdi = "";
        switch( p_act->data.jtag_set.ntrst )
        {
          case HKP_DRV_JTAG_LOW: ntrst = "-"; break;
          case HKP_DRV_JTAG_HIGH: ntrst = "+"; break;
        }
        switch( p_act->data.jtag_set.tms )
        {
          case HKP_DRV_JTAG_LOW: tms = "-"; break;
          case HKP_DRV_JTAG_HIGH: tms = "+"; break;
        }
        switch( p_act->data.jtag_set.tck )
        {
          case HKP_DRV_JTAG_LOW: tck = "-"; break;
          case HKP_DRV_JTAG_HIGH: tck = "+"; break;
        }
        switch( p_act->data.jtag_set.tdi )
        {
          case HKP_DRV_JTAG_LOW: tdi = "-"; break;
          case HKP_DRV_JTAG_HIGH: tdi = "+"; break;
        }
        printf( "  action%s: JTAG set: nTRST=%s TMS=%s TCK=%s TDI=%s\n",
                prompt_str, ntrst, tms, tck, tdi );
      }
      break;
    case hkp_drv_act_out_a:
      printf( "  action%s: output port A: %d = 0x%02X\n",
               prompt_str, p_act->data.out.x, p_act->data.out.x );
      break;
    case hkp_drv_act_out_b:
      printf( "  action%s: output port B: %d = 0x%02X\n",
              prompt_str, p_act->data.out.x, p_act->data.out.x );
      break;
    case hkp_drv_act_out_all:
      printf( "  action%s: output port A: %d = 0x%02X\n"
              "  action%s: output port B: %d = 0x%02X\n",
              prompt_str, p_act->data.out_all.a, p_act->data.out_all.a,
              prompt_str, p_act->data.out_all.b, p_act->data.out_all.b );
      break;
    case hkp_drv_act_pic_get:
      printf( "  action%s: PIC get: PGD=%s\n",
              prompt_str, p_act->data.pic_get.pgd == HKP_DRV_PIC_PGD_HIGH ? "+" : "-" );
      break;
    case hkp_drv_act_pic_in:
      printf( "  action%s: PIC input: cnt=%d value=%d=0x%04X\n",
              prompt_str, p_act->data.pic_out_in.cnt,
              p_act->data.pic_out_in.data, p_act->data.pic_out_in.data );
      break;
    case hkp_drv_act_pic_out:
      printf( "  action%s: PIC output: cnt=%d value=%d=0x%04X\n",
              prompt_str, p_act->data.pic_out_in.cnt,
              p_act->data.pic_out_in.data, p_act->data.pic_out_in.data );
      break;
    case hkp_drv_act_pic_set:
      {
        char * vpp = "", * pgc = "", * pgd = "";
        switch( p_act->data.pic_set.vpp )
        {
          case HKP_DRV_PIC_VPP_0V: vpp = "0V"; break;
          case HKP_DRV_PIC_VPP_13V: vpp = "13V"; break;
          case HKP_DRV_PIC_VPP_OPEN: vpp = "*"; break;
        }
        switch( p_act->data.pic_set.pgc )
        {
          case HKP_DRV_PIC_PGC_LOW: pgc = "-"; break;
          case HKP_DRV_PIC_PGC_HIGH: pgc = "+"; break;
        }
        switch( p_act->data.pic_set.pgd )
        {
          case HKP_DRV_PIC_PGD_LOW: pgd = "-"; break;
          case HKP_DRV_PIC_PGD_HIGH: pgd = "+"; break;
          case HKP_DRV_PIC_PGD_OPEN: pgd = "*"; break;
        }
        printf( "  action%s: PIC set: VPP=%s PGC=%s PGD=%s\n",
                prompt_str, vpp, pgc, pgd );
      }
      break;
    case hkp_drv_act_pwm_a:
      printf( "  action%s: PWM output A: %d (= %2d.%d%%)\n",
              prompt_str, p_act->data.pwm.x, p_act->data.pwm.x / 10, p_act->data.pwm.x % 10 );
      break;
    case hkp_drv_act_pwm_b:
      printf( "  action%s: PWM output B: %d (= %2d.%d%%)\n",
              prompt_str, p_act->data.pwm.x, p_act->data.pwm.x / 10, p_act->data.pwm.x % 10 );
      break;
    case hkp_drv_act_pwm_all:
      printf( "  action%s: PWM output A: %d (= %2d.%d%%)\n"
              "  action%s: PWM output B: %d (= %2d.%d%%)\n",
              prompt_str, p_act->data.pwm_all.a, p_act->data.pwm_all.a / 10, p_act->data.pwm_all.a % 10,
              prompt_str, p_act->data.pwm_all.b, p_act->data.pwm_all.b / 10, p_act->data.pwm_all.b % 10 );
      break;
    case hkp_drv_act_pwm_speed:
      printf( "  action%s: PWM speed: %d (= %dHz)\n",
              prompt_str, p_act->data.pwm_speed.freq, (unsigned int)p_act->data.pwm_speed.freq * 250 );
      break;
  }
}



//execute an action
static void cmd_act( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_act act;
  int err;

  //read an action
  if( act_read( "", &act, quiet ) != 0 )
    return;

  //execute action
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_act, &act );

  //show error
  if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
    return;
  }

  //show result of action
  act_result( "", &act, quiet );
}

//execute multiple actions
static void cmd_acts( int fd_hkp_drv, char * p_params, int quiet )
{
  char * p_param, act_no_str[16];
  int act_cnt, i, err;
  struct t_hkp_drv_act * p_acts;
  struct t_hkp_drv_acts acts;

  //get number of actions
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%i", &act_cnt );
  if( i != 1 || act_cnt < 1 || act_cnt > 255 )
  {
    printf( "  error: invalid action count \"%s\"\n", p_param );
    return;
  }

  //allocate array for actions
  p_acts = (struct t_hkp_drv_act *)malloc( act_cnt * sizeof( struct t_hkp_drv_act ) );
  if( p_acts == NULL )
  {
    printf( "  error: out of memory\n" );
    return;
  }

  //read actions
  for( i = 0; i < act_cnt; i++ )
  {
    sprintf( act_no_str, " %d", i );
    if( act_read( act_no_str, &p_acts[i], quiet ) != 0 )
      act_cnt = i;
  }
  if( i == 0 )
  {
    free( p_acts );
    return;
  }

  //execute actions
  acts.cnt = act_cnt;
  acts.p_acts = p_acts;
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_acts, &acts );

  //show error
  if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
    free( p_acts );
    return;
  }
  
  //show results of actions
  for( i = 0; i < act_cnt; i++ )
  {
    sprintf( act_no_str, " %d", i );
    act_result( act_no_str, &p_acts[i], quiet );
  }

  //free array with actions
  free( p_acts );
}

//read hardware version
static void cmd_hw_ver( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_hw_ver hw_ver;
  int err;

  //get hardware version
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_hw_ver, &hw_ver );

  //show error
  if( err != 0 )
    printf( "  error: %s\n", strerror( errno ) );
  //show hardware version
  else
    printf( "  hardware variant code: %02X\n"
            "  hardware version: %d.%d.%d\n"
            "  hardware date: %04d-%02d-%02d\n",
            hw_ver.variant,
            hw_ver.major, hw_ver.minor, hw_ver.revision,
            hw_ver.year, hw_ver.month, hw_ver.day );
}

//get identification of hacker port
static void cmd_id_get( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_id id;
  int err, i;

  //get identification
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_get_id, &id );

  //show error
  if( err != 0 )
    printf( "  error: %s\n", strerror( errno ) );
  //show identification
  else
  {
    printf( "  identification: " );
    for( i = 0; i < sizeof( id.id ); i++ )
      if( (char)id.id[i] >= ' ' )
        printf( "%c", (char)id.id[i] );
      else
        printf( " " );
    printf( "\n" );
  }
}

//set identification of hacker port
static void cmd_id_set( int fd_hkp_drv, char * p_params, int quiet )
{
  char * p_param;
  struct t_hkp_drv_id id;
  int i, err;

  //get identification from parameter
  str_first_word( p_params, &p_param, &p_params );
  for( i = 0; p_param[i] != 0 && i < sizeof( id.id ); i++ )
    id.id[i] = (unsigned char)p_param[i];
  for( ; i < sizeof( id.id ); i++ )
    id.id[i] = (unsigned char)' ';

  //set identification
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_set_id, &id );

  //show error
  if( err != 0 )
    printf( "  error: %s\n", strerror( errno ) );
}

//request / release ownership of ports
//{[+-]out_[ab]} [[+-]in_[ab]} {[+-]pwm_[ab]} {[+-]analog_[a-e]} {[+-]ser} {[+-]i2c} {[+-]pic} {[+-]jtag}
static void cmd_own( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_own own;
  char * p_param, flag;
  int i, err;

  //initialize port ownership request structure
  for( i = 0; i < count( own.out ); i++ )
    own.out[i] = 0;
  for( i = 0; i < count( own.in ); i++ )
    own.in[i] = 0;
  for( i = 0; i < count( own.pwm ); i++ )
    own.pwm[i] = 0;
  for( i = 0; i < count( own.analog ); i++ )
    own.analog[i] = 0;
  own.ser = 0;
  own.i2c = 0;
  own.pic = 0;
  own.jtag = 0;

  //set up port ownership request structure
  for( ; ; )
  {
    //get next parameter
    str_first_word( p_params, &p_param, &p_params );
    if( p_param[0] == 0 )
      break;

    //plus or minus sign found
    if( p_param[0] == '+' || p_param[0] == '-' )
    {
      //get flag if to own or free port
      flag = (p_param[0] == '+') ? 1 : -1;

      //set flags for ports
      if( strncasecmp( &p_param[1], "out_", 4 ) == 0 )
        for( i = 5; p_param[i] != 0; i++ ) {
          if( p_param[i] >= 'A' && p_param[i] < 'A' + count( own.out ) )
            own.out[p_param[i] - 'A'] = flag;
          if( p_param[i] >= 'a' && p_param[i] < 'a' + count( own.out ) )
            own.out[p_param[i] - 'a'] = flag; }
      if( strncasecmp( &p_param[1], "in_", 3 ) == 0 )
        for( i = 4; p_param[i] != 0; i++ ) {
          if( p_param[i] >= 'A' && p_param[i] < 'A' + count( own.in ) )
            own.in[p_param[i] - 'A'] = flag;
          if( p_param[i] >= 'a' && p_param[i] < 'a' + count( own.in ) )
            own.in[p_param[i] - 'a'] = flag; }
      if( strncasecmp( &p_param[1], "pwm_", 4 ) == 0 )
        for( i = 5; p_param[i] != 0; i++ ) {
          if( p_param[i] >= 'A' && p_param[i] < 'A' + count( own.pwm ) )
            own.pwm[p_param[i] - 'A'] = flag;
          if( p_param[i] >= 'a' && p_param[i] < 'a' + count( own.pwm ) )
            own.pwm[p_param[i] - 'a'] = flag; }
      if( strncasecmp( &p_param[1], "analog_", 7 ) == 0 )
        for( i = 8; p_param[i] != 0; i++ ) {
          if( p_param[i] >= 'A' && p_param[i] < 'A' + count( own.analog ) )
            own.analog[p_param[i] - 'A'] = flag;
          if( p_param[i] >= 'a' && p_param[i] < 'a' + count( own.analog ) )
            own.analog[p_param[i] - 'a'] = flag; }
      if( strncasecmp( &p_param[1], "ser", 3 ) == 0 )
        own.ser = flag;
      if( strncasecmp( &p_param[1], "i2c", 3 ) == 0 )
        own.i2c = flag;
      if( strncasecmp( &p_param[1], "pic", 3 ) == 0 )
        own.pic = flag;
      if( strncasecmp( &p_param[1], "jtag", 4 ) == 0 )
        own.jtag = flag;
    }
  }

  //request / release ownership
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_own, &own );

  //show error
  if( err != 0 )
  {
    printf( "  error: %s\n", strerror( errno ) );
    return;
  }

  //show owned ports
  printf( "  owned output ports:" );
  for( i = 0; i < count( own.out ); i++ )
    if( own.out[i] > 0 )
      printf( " out_%c", i + 'A' );
  printf( "\n" );
  printf( "  owned input ports:" );
  for( i = 0; i < count( own.in ); i++ )
    if( own.in[i] > 0 )
      printf( " in_%c", i + 'A' );
  printf( "\n" );
  printf( "  owned PWM outputs:" );
  for( i = 0; i < count( own.pwm ); i++ )
    if( own.pwm[i] > 0 )
      printf( " pwm_%c", i + 'A' );
  printf( "\n" );
  printf( "  owned analog inputs:" );
  for( i = 0; i < count( own.analog ); i++ )
    if( own.analog[i] > 0 )
      printf( " analog_%c", i + 'A' );
  printf( "\n" );
  if( own.ser > 0 )
    printf( "  serial port owned\n" );
  if( own.i2c > 0 )
    printf( "  I2C bus owned\n" );
  if( own.pic > 0 )
    printf( "  PIC programming interface owned\n" );
  if( own.jtag > 0 )
    printf( "  JTAG master port owned\n" );
}

//set baudrate of serial port
static void cmd_ser_baud( int fd_hkp_drv, char * p_params, int quiet )
{
  char * p_param;
  long baud;
  int i, err;
  struct t_hkp_drv_ser_baud ser_baud;

  //get baudrate
  str_first_word( p_params, &p_param, &p_params );
  i = sscanf( p_param, "%li", &baud );
  if( i != 1 || baud < 1 || baud > 1000000 )
  {
    printf( "  error: invalid baudrate \"%s\"\n", p_param );
    return;
  }

  //calculate bit time
  ser_baud.bit_time = (10000000 + baud / 2) / baud;

  //set baudrate
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_ser_baud, &ser_baud );

  //show error
  if( err != 0 )
    printf( "  error: %s\n", strerror( errno ) );
}

//receive some bytes from serial port
static void cmd_ser_recv( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_ser_recv ser_recv;
  int i, err, finished;

  for( ; ; )
  {
    //fetch data
    err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_ser_recv, &ser_recv );
    //show error
    if( err != 0 )
    {
      printf( "  error: %s\n", strerror( errno ) );
      break;
    }

    finished = 1;

    //show serial port errors
    if( ser_recv.err_frame || ser_recv.err_ov_hw || ser_recv.err_ov_fw || ser_recv.err_ov_drv )
    {
      printf( "  serial port error(s):\n" );
      if( ser_recv.err_frame )
        printf( "    framing error\n" );
      if( ser_recv.err_ov_hw )
        printf( "    overflow error in hardware\n" );
      if( ser_recv.err_ov_fw )
        printf( "    overflow error in firmware\n" );
      if( ser_recv.err_ov_drv )
        printf( "    overflow error in driver\n" );
      finished = 0;
    }

    //show data
    if( ser_recv.len > 0 )
    {
      printf( "  data:" );
      for( i = 0; i < ser_recv.len; i++ )
        printf( " %02X", ser_recv.data[i] );
      printf( "\n" );
      finished = 0;
    }

    //finished if no error and no data
    if( finished )
      break;
  }
}

//send some bytes using serial port
static void cmd_ser_send( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_ser_send ser_send;
  char * p_param;
  int i, val, err;

  //process all data bytes in paramaters
  for( ; ; )
  {
    //initialize send structure
    ser_send.len = 0;

    //get data bytes
    while( ser_send.len < count( ser_send.data ) )
    {
      //get next data byte
      str_first_word( p_params, &p_param, &p_params );
      if( p_param[0] == 0 ) //no more data bytes
        break;
      i = sscanf( p_param, "%i", &val );
      if( i != 1 || val < 0 || val > 255 )
      {
        printf( "  error: invalid data byte \"%s\"\n", p_param );
        return;
      }
      //save data byte
      ser_send.data[ser_send.len++] = (unsigned char)val;
    }

    //no more data to send
    if( ser_send.len == 0 )
      break;

    //send data
    err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_ser_send, &ser_send );
    //show error
    if( err != 0 )
    {
      printf( "  error: %s\n", strerror( errno ) );
      break;
    }

    //show sent data
    if( !quiet )
    {
      printf( "  sent:" );
      for( i = 0; i < ser_send.len; i++ )
        printf( " %02X", ser_send.data[i] );
      printf( "\n" );
    }
  }
}

//get version of driver
static void cmd_ver( int fd_hkp_drv, char * p_params, int quiet )
{
  struct t_hkp_drv_ver ver;
  int err;

  //get driver version
  err = ioctl( fd_hkp_drv, hkp_drv_ioctl_no_ver, &ver );

  //show error
  if( err != 0 )
    printf( "  error: %s\n", strerror( errno ) );
  //show driver version
  else
    printf( "  driver version: %d.%d.%d\n"
            "  driver date: %04d-%02d-%02d\n",
            ver.major, ver.minor, ver.revision,
            ver.year, ver.month, ver.day );
}



//help command prototype
static void cmd_help( int fd_hkp_drv, char * p_params, int quiet );

//command table
struct t_cmd_tab
{
  char * name;
  char * parameters;
  char * description;
  void (* function)( int fd_hkp_drv, char * p_params, int quiet );
} cmd_tab[] =
{
  { "act", "", "execute an action", cmd_act },
  { "acts", " <count>", "execute multiple actions", cmd_acts },
  { "end", "", "end the program", NULL },
  { "exit", "", "end the program", NULL },
  { "help", " {<command>}", "show help (you already found out what this does)", cmd_help },
  { "hw_ver", "", "read hardware version", cmd_hw_ver },
  { "id_get", "", "get identification of hacker port", cmd_id_get },
  { "id_set", " <identification>", "set identification of hacker port", cmd_id_set },
  { "own", " {[+-]out_[AB]} [[+-]in_[AB]} {[+-]pwm_[AB]} {[+-]analog_[A-E]} {[+-]ser} {[+-]i2c} {[+-]pic} {[+-]jtag}", "request / release ownership of ports", cmd_own },
  { "quit", "", "end the program", NULL },
  { "ser_baud", " <baudrate>", "set baudrate of serial port", cmd_ser_baud },
  { "ser_recv", "", "receive some bytes from serial port", cmd_ser_recv },
  { "ser_send", " <data> ...", "send some bytes using serial port", cmd_ser_send },
  { "ver", "", "show driver version", cmd_ver },
};

//help command
static void cmd_help( int dhkp_drv, char * p_params, int quiet )
{
  char * p_cmd;
  int i;

  //get command
  str_first_word( p_params, &p_cmd, &p_params );

  //command found in parameters
  if( p_cmd[0] != 0 )
  {
    //search command
    for( i = 0; i < count( cmd_tab ); i++ )
      if( strcasecmp( p_cmd, cmd_tab[i].name ) == 0 )
        break;
    //command found in table
    if( i < count( cmd_tab ) )
      //print help for this command
      printf( "  %s%s\n    %s\n", cmd_tab[i].name, cmd_tab[i].parameters, cmd_tab[i].description );
    //command not found in table
    else
      //print error message
      printf( "  no help found to \"%s\"\n", p_cmd );
    return;
  }

  //print command table and descriptions
  for( i = 0; i < count( cmd_tab ); i++ )
    printf( "  %s%s\n    %s\n", cmd_tab[i].name, cmd_tab[i].parameters, cmd_tab[i].description );
}



//main program
int main( int arg_cnt, char * * args )
{
  int quiet, fd_hkp_drv, i;
  char line[1024], * p_chr, * p_cmd, * p_params;

  //get quiet flag
  quiet = arg_cnt >= 3 && strcasecmp( args[2], "quiet" ) == 0;

  //print message
  if( ! quiet )
    printf( "HackerPort driver for Linux - test application\n"
            "version 0.7.2 date 2007-05-04\n"
            "part of the hacker port project - http[s]://stefan.schuermans.info/hackerport/\n"
            "Copyright (C) 2003-2005 stefan <stefan@schuermans.info>\n"
            "Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html\n" );

  //check parameters
  if( arg_cnt < 2 )
  {
    printf( "  usage: %s <hacker port device> [quiet]\n", args[0] );
    return -1;
  }

  //open hacker port
  fd_hkp_drv = open( args[1], O_RDWR );
  if( fd_hkp_drv == -1 )
  {
    printf( "  could not open hacker port device\"%s\": %s\n", args[1], strerror( errno ) );
    return -1;
  }

  //main loop
  for( ; ; )
  {
    //read a line
    if( ! quiet ) //prompt
    {
      printf( "hacker port> " );
      fflush( stdout );
    }
    line[0] = 0; //read line
    fgets( line, sizeof( line ), stdin );
    p_chr = strchr( line, '\n' ); //remove newline
    if( p_chr != NULL )
      *p_chr = 0;

    //get command (first word of line)
    str_first_word( line, &p_cmd, &p_params );

    //ignore empty command
    if( p_cmd[0] != 0 )
    {
      //find command in command table
      for( i = 0; i < count( cmd_tab ); i++ )
        if( strcasecmp( p_cmd, cmd_tab[i].name ) == 0 )
          break;
      //command found
      if( i < count( cmd_tab ) )
      {
        //exit main loop if no function
        if( cmd_tab[i].function == NULL )
          break;
        //call command
        cmd_tab[i].function( fd_hkp_drv, p_params, quiet );
      }
      //command not found
      else
        printf( "  unknown command - type \"help\"\n" );
    }
  }

  //close hacker port
  close( fd_hkp_drv );

  return 0;
}

